package com.serial.comm.core.telegram.utils;

import org.springframework.stereotype.Component;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.*;
import java.util.Arrays;

@Component
public final class ITA2Util extends Charset {
    private static final String NAME = "ITA2Charset";

    private static final String[] ALIASES = new String[]{"ITA2"};

    public CharsetDecoder getDecoder(){
        return newDecoder();
    }

    public CharsetDecoder newDecoder() {
        return new Decoder(this);
    }

    public CharsetEncoder newEncoder() {
        return new Encoder(this);
    }

    private enum ShiftState {
        FIGURE((byte) 27),
        LETTER((byte) 31);

        private final byte shiftByte;

        ShiftState(byte value) {
            this.shiftByte = value;
        }

        public byte getShiftByte() {
            return this.shiftByte;
        }
    }

    private static final char[] DECODE_TO_LETTER_LSB = new char[]{
            Character.MIN_VALUE, 'E', '\n', 'A', ' ', 'S', 'I', 'U', '\r', 'D',
            'R', 'J', 'N', 'F', 'C', 'K', 'T', 'Z', 'L', 'W',
            'H', 'Y', 'P', 'Q', 'O', 'B', 'G', Character.MIN_VALUE, 'M', 'X',
            'V', Character.MIN_VALUE};

    private static final char[] DECODE_TO_FIGURE_LSB = new char[]{
            Character.MIN_VALUE, '3', '\n', '-', ' ', '\'', '8', '7', '\r', '$',
            '4', '\007', ',', '!', ':', '(', '5', '+', ')', '2',
            '#', '6', '0', '1', '9', '?', '&', Character.MIN_VALUE, '.', '/',
            '=', Character.MIN_VALUE};

    private static final char[] DECODE_TO_LETTER_MSB = new char[]{
            Character.MIN_VALUE, 'T', '\r', 'O', ' ', 'H', 'N', 'M', '\n', 'L',
            'R', 'G', 'I', 'P', 'C', 'V', 'E', 'Z', 'D', 'B',
            'S', 'Y', 'F', 'X', 'A', 'W', 'J', Character.MIN_VALUE, 'U', 'Q',
            'K', Character.MIN_VALUE};

    private static final char[] DECODE_TO_FIGURE_MSB = new char[]{
            Character.MIN_VALUE, '5', '\r', '9', ' ', '#', ',', '.', '\n', ')',
            '4', '&', '8', '0', ':', '=', '3', '+', '$', '?',
            '\'', '6', '!', '/', '-', '2', '\007', Character.MIN_VALUE, '7', '1',
            '(', Character.MIN_VALUE};

    private static final byte[] ENCODE_MSB = new byte[128];

    private static final byte[] ENCODE_LSB = new byte[128];

    static {
        if (DECODE_TO_FIGURE_LSB.length != 32 || DECODE_TO_FIGURE_MSB.length != 32 || DECODE_TO_LETTER_LSB.length != 32 || DECODE_TO_LETTER_MSB.length != 32)
            throw new IllegalStateException("ITA2Charset not setup correctly");
        byte fill = -1;
        Arrays.fill(ENCODE_LSB, fill);
        Arrays.fill(ENCODE_MSB, fill);
    }

    private static final ShiftState[] ENCODE_SHIFT_STATE = new ShiftState[128];

    private final char[] decodeLetter;

    private final char[] decodeFigure;

    private final byte[] encodeArray;

    private final ShiftState[] encodeShiftState;

    static {
        int[] skipState = {0, 2, 4, 8, 27, 31};
        int i;
        for (i = 0; i < 32; i++) {
            if (i != 27 && i != 31) {
                boolean skip = (Arrays.binarySearch(skipState, i) >= 0);
                int index = DECODE_TO_LETTER_LSB[i];
                if (index > 127)
                    throw new IllegalStateException("ITA2Charset not setup correctly");
                ENCODE_LSB[index] = (byte) i;
                if (!skip)
                    ENCODE_SHIFT_STATE[index] = ShiftState.LETTER;
                index = DECODE_TO_FIGURE_LSB[i];
                if (index > 127)
                    throw new IllegalStateException("ITA2Charset not setup correctly");
                ENCODE_LSB[index] = (byte) i;
                if (!skip)
                    ENCODE_SHIFT_STATE[index] = ShiftState.FIGURE;
            }
        }
        for (i = 0; i < 32; i++) {
            int index = DECODE_TO_LETTER_MSB[i];
            if (index > 127)
                throw new IllegalStateException("ITA2Charset not setup correctly");
            ENCODE_MSB[index] = (byte) i;
            index = DECODE_TO_FIGURE_MSB[i];
            if (index > 127)
                throw new IllegalStateException("ITA2Charset not setup correctly");
            ENCODE_MSB[index] = (byte) i;
        }
    }

    public ITA2Util() {
        this(false);
    }

    public ITA2Util(boolean isBigEndian) {
        super("ITA2Charset", ALIASES);
        if (isBigEndian) {
            this.decodeLetter = DECODE_TO_LETTER_MSB;
            this.decodeFigure = DECODE_TO_FIGURE_MSB;
            this.encodeArray = ENCODE_MSB;
        } else {
            this.decodeLetter = DECODE_TO_LETTER_LSB;
            this.decodeFigure = DECODE_TO_FIGURE_LSB;
            this.encodeArray = ENCODE_LSB;
        }
        this.encodeShiftState = ENCODE_SHIFT_STATE;
    }

    public boolean contains(Charset cs) {
        return getClass().isInstance(cs);
    }

    public boolean canEncode() {
        return true;
    }



    private final class Encoder extends CharsetEncoder {
        private ITA2Util.ShiftState state;

        protected Encoder(ITA2Util cs) {
            super(cs, 1.0F, 2.0F, new byte[]{0});
            reset();
        }

        protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) {
            int remaining = in.remaining();
            while (remaining-- > 0) {
                if (out.remaining() < 1)
                    return CoderResult.OVERFLOW;
                char c = in.get();
                if (!isBasicLatin(c)) {
                    in.position(in.position() - 1);
                    return CoderResult.unmappableForLength(1);
                }
                c = Character.toUpperCase(c);
                byte value = ITA2Util.this.encodeArray[c];
                if (value == -1) {
                    in.position(in.position() - 1);
                    return CoderResult.unmappableForLength(1);
                }
                ITA2Util.ShiftState newState = ITA2Util.this.encodeShiftState[c];
                if (newState != null && this.state != newState) {
                    if (out.remaining() < 2) {
                        in.position(in.position() - 1);
                        return CoderResult.OVERFLOW;
                    }
                    out.put(newState.getShiftByte());
                    this.state = newState;
                }
                out.put(value);
            }
            return CoderResult.UNDERFLOW;
        }

        public boolean isLegalReplacement(byte[] repl) {
            for (byte value : repl) {
                if (value < 0 || value > 31)
                    return false;
            }
            return true;
        }

        protected void implReset() {
            this.state = ITA2Util.ShiftState.LETTER;
        }

        private final boolean isBasicLatin(char c) {
            int codePoint = c;
            if (codePoint >= 0 && codePoint < 128)
                return true;
            return false;
        }
    }

    protected final class Decoder extends CharsetDecoder {
        private ITA2Util.ShiftState state;

        protected Decoder(ITA2Util cs) {
            super(cs, 1.0F, 1.0F);
            reset();
        }

        protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
            int remaining = in.remaining();
            while (remaining-- > 0) {
                char value;
                if (out.remaining() < 1)
                    return CoderResult.OVERFLOW;
                byte b = in.get();
                if (!isITA2byte(b)) {
                    in.position(in.position() - 1);
                    return CoderResult.unmappableForLength(1);
                }
                if (b == 31) {
                    this.state = ITA2Util.ShiftState.LETTER;
                    continue;
                }
                if (b == 27) {
                    this.state = ITA2Util.ShiftState.FIGURE;
                    continue;
                }
                if (this.state == ITA2Util.ShiftState.LETTER) {
                    value = ITA2Util.this.decodeLetter[b];
                } else {
                    value = ITA2Util.this.decodeFigure[b];
                }
                out.put(value);
            }
            return CoderResult.UNDERFLOW;
        }

        protected void implReset() {
            this.state = ITA2Util.ShiftState.LETTER;
        }

        private final boolean isITA2byte(byte b) {
            if (b >= 0 && b < 32)
                return true;
            return false;
        }
    }

    public static void main(String[] args) throws CharacterCodingException {
        ITA2Util cs = new ITA2Util();
        //自定义报文
        String sourceMsg = "TDY CA8234 FLT SZX-WUH\n" +
                "WE OFLD CGO\n" +
                "CN65260495 13PCS\n" +
                "请给予货主先到先提\n" +
                "TKS COOP STP";
        ByteBuffer sourceBytes = cs.newEncoder().encode(CharBuffer.wrap(sourceMsg.toCharArray()));

        System.out.println("原始字节码：" + Arrays.toString(new byte[sourceBytes.remaining()]));
        CharBuffer result = cs.newDecoder().decode(sourceBytes);
        System.out.println("手动转码结果: " + result.toString());



        //真实报文
        byte[] bytes = {31, 31, 31, 31, 31, 17, 14, 17, 14, 4, 9, 13, 14, 27, 22, 1, 1, 1, 4, 19, 22, 23, 16, 19, 22, 4, 8, 2, 31, 23, 9, 4, 19, 7, 20, 13, 6, 27, 6, 31, 29, 8, 2, 27, 28, 31, 9, 29, 25, 13, 28, 16, 26, 4, 27, 19, 22, 23, 16, 23, 10, 8, 2, 31, 13, 5, 7, 27, 29, 23, 19, 8, 2, 24, 24, 24, 3, 7, 6, 23, 23, 7, 16, 22, 19, 31, 22, 30, 26, 25, 15, 15, 27, 29, 31, 16, 27, 23, 31, 15, 27, 10, 24, 21, 28, 22, 8, 2, 31, 9, 18, 30, 27, 29, 19, 22, 31, 3, 7, 26, 27, 19, 22, 23, 19, 29, 31, 25, 15, 15, 27, 29, 31, 16, 27, 23, 31, 15, 27, 10, 24, 21, 28, 22, 29, 31, 3, 8, 2, 27, 30, 8, 2, 8, 2, 2, 2, 2, 2, 2, 2, 2, 31, 12, 12, 12, 12, 31, 31, 31, 31};
        String inStr = new String(bytes);
        System.out.println("真实报文：Byte数组转" + inStr);
        CharBuffer resultAct = cs.newDecoder().decode(ByteBuffer.wrap(bytes));
        System.out.println("真实报文转码结果: " + resultAct);




    }
}
